package com.wmx.www.controller;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.ZipUtil;
import org.apache.commons.compress.archivers.ArchiveEntry;
import org.apache.commons.compress.archivers.zip.Zip64Mode;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;
import org.springframework.http.*;
import org.springframework.stereotype.Controller;
import org.springframework.util.Assert;
import org.springframework.util.StreamUtils;
import org.springframework.web.bind.annotation.*;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URL;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * Spring Boot 文件下载方式汇总
 *
 * @author wangMaoXiong
 * Created by Administrator on 2019/3/17 0017.
 */
@Controller
@SuppressWarnings("Duplicates")
public class DownloadFileController {

    private static final Logger log = LoggerFactory.getLogger(DownloadFileController.class);

    private static final Map<String, String> imageMap = new HashMap<>();
    private static final Map<String, String> videoMap = new HashMap<>();

    static {
        imageMap.put("1", "static/images/气质美女.jpg");
        imageMap.put("2", "static/images/凶猛老虎.jpg");

        videoMap.put("3", "static/videos/演戏连跑步都需要假跑吗.mp4");
        videoMap.put("4", "static/videos/美女路边直播,路过家长翻了一个白眼.mp4");
    }

    /**
     * 单文件下载
     * 1、使用原生 InputStream 流读写文件。
     * 2、支持任意格式的文件。
     * http://localhost:8080/fileServer/singleDownload1?fileId=1
     * http://localhost:8080/fileServer/singleDownload1?fileId=2
     *
     * @param response
     * @param fileId   ：被下载文件ID
     * @throws IOException
     */
    @RequestMapping(value = {"/singleDownload1"}, method = {RequestMethod.GET})
    public void singleDownload1(HttpServletResponse response, String fileId) throws Exception {
        log.debug("文件下载开始：{}", fileId);
        // 构建文件输入流(推荐使用：org.apache.commons.io.FileUtils.readFileToByteArray)
        File downloadFile = this.getFile(fileId);
        // 获取文件的媒体类型
        String contentType = Files.probeContentType(downloadFile.toPath());
        if (contentType == null) {
            // 获取失败时使用通用的文件类型(application/octet-stream)
            contentType = MediaType.APPLICATION_OCTET_STREAM_VALUE;
        }
        // 原生IO流读取方式
        try (InputStream inputStream = new FileInputStream(downloadFile);
             OutputStream outputStream = new BufferedOutputStream(response.getOutputStream())) {
            String filename = downloadFile.getName();
            // 设置返回类型，如果是返回普通文本信息，则也可以使用：response.setContentType("text/html;charset=utf-8");
            response.setContentType(contentType);
            // 设置文件内容大小
            response.setContentLengthLong(Files.size(downloadFile.toPath()));
            // 设置返回的头信息，对文件名称进行编码，否则中文容易乱码。
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(filename, "UTF-8"));
            int length;
            byte[] bytes = new byte[1024];
            while ((length = inputStream.read(bytes)) != -1) {
                outputStream.write(bytes, 0, length);
            }
            outputStream.flush();
            outputStream.close();
            log.info("文件下载完成：{}", fileId);
        }
    }

    /**
     * 单文件下载
     * 1、使用第三方API快速读写，如 Apache 的工具类 FileUtils、IOUtils：
     * * org.apache.commons.io.FileUtils.readFileToByteArray 读取文件字节内容.
     * * org.apache.commons.io.IOUtils#write(byte[], java.io.OutputStream)
     * * org.apache.commons.io.IOUtils#copy(java.io.InputStream, java.io.OutputStream)
     * 2、支持任意格式的文件。
     * http://localhost:8080/fileServer/singleDownload2?fileId=2
     *
     * @param response
     * @param fileId   ：被下载文件ID
     * @return
     */
    @RequestMapping(value = {"/singleDownload2"}, method = {RequestMethod.GET})
    public void singleDownload2(HttpServletResponse response, String fileId) throws Exception {
        try (OutputStream outputStream = new BufferedOutputStream(response.getOutputStream())) {
            log.debug("文件下载开始：{}", fileId);
            File downloadFile = this.getFile(fileId);
            // 获取文件的媒体类型
            String contentType = Files.probeContentType(downloadFile.toPath());
            if (contentType == null) {
                // 获取失败时使用通用的文件类型(application/octet-stream)
                contentType = MediaType.APPLICATION_OCTET_STREAM_VALUE;
            }
            String filename = downloadFile.getName();
            // 设置返回类型，如果是返回普通文本信息，则也可以使用：response.setContentType("text/html;charset=utf-8");
            response.setContentType(contentType);
            // 设置文件内容大小
            response.setContentLengthLong(Files.size(downloadFile.toPath()));
            // 设置返回的头信息，对文件名称进行编码，否则中文容易乱码。
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(filename, "UTF-8"));
            // 构建文件输入流(推荐使用org.apache.commons.io.FileUtils.readFileToByteArray)
            byte[] byteArray = FileUtils.readFileToByteArray(downloadFile);
            // org.apache.commons.io.IOUtils.write(byteArray, response.getOutputStream());
            // 写入到输出流返回给客户端
            outputStream.write(byteArray);
        }
        log.info("文件下载完成：{}", fileId);
    }

    /**
     * 单文件下载
     * 1、使用 Jdk8 的 nio 的原生 API：Files、Paths API 读写文件
     * 2、支持任意格式的文件。
     * http://localhost:8080/fileServer/singleDownload3?fileId=2
     *
     * @param response
     * @param fileId   ：被下载文件ID
     * @return
     */
    @RequestMapping(value = {"/singleDownload3"}, method = {RequestMethod.GET})
    public void singleDownload3(HttpServletResponse response, String fileId) {
        OutputStream outputStream = null;
        try {
            outputStream = response.getOutputStream();
            // 获取被下载文件
            File downloadFile = this.getFile(fileId);
            Path downloadFilePath = downloadFile.toPath();
            String filename = downloadFile.getName();
            // 获取文件的媒体类型
            String contentType = Files.probeContentType(downloadFilePath);
            if (contentType == null) {
                // 获取失败时使用通用的文件类型(application/octet-stream)
                contentType = MediaType.APPLICATION_OCTET_STREAM_VALUE;
            }
            response.setContentType(contentType);
            // 文件大小
            response.setContentLengthLong(Files.size(downloadFilePath));
            // 设置返回的头信息(Content-Disposition)，对文件名称进行编码，否则中文容易乱码。
            response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + URLEncoder.encode(filename, "UTF-8"));
            // 响应数据给客户端
            Files.copy(downloadFilePath, outputStream);
        } catch (Exception e) {
            log.error(ExceptionUtils.getStackTrace(e));
            if (outputStream != null) {
                try {
                    outputStream.flush();
                    outputStream.close();
                } catch (IOException e1) {
                    log.error(ExceptionUtils.getStackTrace(e1));
                }
            }
        }
    }

    /**
     * http://localhost:8080/fileServer/singleDownload4?fileId=2
     * <p>
     * 第三方 hutool 的工具类 FileUtil、IoUtil：
     * cn.hutool.core.io.IoUtil#copy(java.io.InputStream, java.io.OutputStream)
     * cn.hutool.core.io.FileUtil.writeToStream(File file, OutputStream out)
     *
     * @param response
     */
    @RequestMapping(value = {"/singleDownload4"}, method = {RequestMethod.GET})
    public void singleDownload4(HttpServletResponse response) {
        OutputStream outputStream = null;
        try {
            String filePath = "static/videos/演戏连跑步都需要假跑吗.mp4";
            String filename = FileUtil.getName(filePath);
            outputStream = response.getOutputStream();
            // 获取文件的媒体类型(application/octet-stream)
            response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
            // 设置返回的头信息(Content-Disposition)，对文件名称进行编码，否则中文容易乱码。
            response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + URLEncoder.encode(filename, "UTF-8"));

            // 方式1
            // InputStream inputStream = DownloadFileController.class.getClassLoader().getResourceAsStream(filePath);
            // cn.hutool.core.io.IoUtil.copy(inputStream, outputStream);

            // 方式2
            URL url = DownloadFileController.class.getClassLoader().getResource(filePath);
            File file = FileUtil.file(url);
            FileUtil.writeToStream(file, outputStream);
        } catch (Exception e) {
            log.error(ExceptionUtils.getStackTrace(e));
            if (outputStream != null) {
                try {
                    outputStream.flush();
                    outputStream.close();
                } catch (IOException e1) {
                    log.error(ExceptionUtils.getStackTrace(e1));
                }
            }
        }
    }

    /**
     * Spring的Resource抽象提供了对不同来源资源的统一访问方式，非常适合文件下载场景。
     * http://localhost:8080/fileServer/responseEntity1/1
     *
     * @param fileId ：类路径下的文件ID
     * @return
     */
    @GetMapping("/responseEntity1/{fileId:.+}")
    public ResponseEntity<Resource> responseEntity1(@PathVariable String fileId) {
        try {
            // 类路径下的文件，资源文件，打成包也能访问.
            URL url = DownloadFileController.class.getClassLoader().getResource(imageMap.get(fileId));
            Resource resource = new UrlResource(url);

            // 电脑上/服务器上的独立文件
            // Path filePath = Paths.get("F:\\Music\\1987我不知会遇见你 - 李宇春.mp3");
            // Resource resource = new UrlResource(filePath.toUri());
            // 检查资源是否存在
            if (resource.exists()) {
                return ResponseEntity.ok()
                        .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + resource.getFilename() + "\"")
                        .contentType(MediaType.APPLICATION_OCTET_STREAM)
                        .body(resource);
            } else {
                return ResponseEntity.notFound().build();
            }
        } catch (Exception e) {
            log.error(ExceptionUtils.getStackTrace(e));
            return ResponseEntity.badRequest().build();
        }
    }

    /**
     * SpringBoot中，ResponseEntity类型可以精确控制HTTP响应，为文件下载提供完善的HTTP头信息。
     * http://localhost:8080/fileServer/responseEntity2/1
     *
     * @param fileId
     * @return
     */
    @GetMapping("/responseEntity2/{fileId:.+}")
    public ResponseEntity<byte[]> responseEntity2(@PathVariable String fileId) {
        try {
            File downloadFile = this.getFile(fileId);
            String fileName = downloadFile.getName();
            byte[] data = Files.readAllBytes(downloadFile.toPath());

            // String fileAddr = "E:\\IDEA_Projects\\material\\doc\\IDEA多实例启动服务.pdf";
            // Path path = Paths.get(fileAddr);
            // String fileName = path.getFileName().toString();
            // byte[] data = Files.readAllBytes(path);

            HttpHeaders headers = new HttpHeaders();
            headers.setContentDisposition(ContentDisposition.builder("attachment").filename(fileName, StandardCharsets.UTF_8).build());
            headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
            // headers.setContentType(MediaType.APPLICATION_PDF);// pdf不这样明确指定，直接指定为上面的流类型也是可以的.
            headers.setContentLength(data.length);

            return new ResponseEntity<>(data, headers, HttpStatus.OK);
        } catch (IOException e) {
            log.error(ExceptionUtils.getStackTrace(e));
            return ResponseEntity.badRequest().build();
        }
    }

    /**
     * http://localhost:8080/fileServer/zipDownload1?fileId=2
     * 多个文件压缩成一个文件后下载.
     * 1、本方法使用原生API进行压缩。
     * 2、也可以使用第三方的解压库：
     * * Apache Commons Compress 文件解压缩库：https://wangmaoxiong.blog.csdn.net/article/details/103943688#文件压缩并提供网络下载
     *
     * @param fileId   ：被下载的文件ID，多个时使用逗号分割，如 1,2,3,4
     * @param response
     * @throws Exception
     */
    @GetMapping("/zipDownload1")
    public void zipDownload1(String fileId, HttpServletResponse response) throws Exception {
        // 要下载的文件列表
        List<Path> listPath = this.listPath(fileId);
        String zipFileName = listPath.get(0).getFileName() + "等" + listPath.size() + "个.zip";
        // 设置发送到客户端的响应的内容类型
        response.setContentType("application/zip");
        // 设置返回的头信息(Content-Disposition)，对文件名称进行编码，否则中文容易乱码。
        response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + URLEncoder.encode(zipFileName, "UTF-8"));
        // 压缩多个文件到zip文件中，并且响应给客户端
        try (ZipOutputStream zipOutputStream = new ZipOutputStream(response.getOutputStream())) {
            for (Path path : listPath) {
                try (InputStream inputStream = Files.newInputStream(path)) {
                    zipOutputStream.putNextEntry(new ZipEntry(path.getFileName().toString()));
                    StreamUtils.copy(inputStream, zipOutputStream);
                    zipOutputStream.flush();
                }
            }
        }
    }

    /**
     * http://localhost:8080/fileServer/zipDownload2?fileId=1,2,3,4
     * 多个文件压缩成一个文件后下载.
     * 1、本方法使用第三方的 hutool ZipUtil 压缩。
     * 2、也可以使用其他第三方的解压库：
     * * Apache Commons Compress 文件解压缩库：https://wangmaoxiong.blog.csdn.net/article/details/103943688#文件压缩并提供网络下载
     *
     * @param fileId   ：被下载的文件ID，多个时使用逗号分割，如 1,2,3,4
     * @param response
     * @throws Exception
     */
    @GetMapping("/zipDownload2")
    public void zipDownload2(String fileId, HttpServletResponse response) throws Exception {
        // 要下载的文件列表
        Map<String, InputStream> inputStreamMap = listInputStream(fileId);
        // 被压缩文件的输入流
        List<InputStream> inputStreams = new ArrayList<>();
        List<String> paths = new ArrayList<>();
        // 压缩包中文件对应的名字
        for (Map.Entry<String, InputStream> entry : inputStreamMap.entrySet()) {
            paths.add(new File(entry.getKey()).getName());
            inputStreams.add(entry.getValue());
        }
        String zipFileName = paths.get(0) + "等" + paths.size() + "个.zip";
        // 设置发送到客户端的响应的内容类型
        response.setContentType("application/zip");
        // 设置返回的头信息(Content-Disposition)，对文件名称进行编码，否则中文容易乱码。
        response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + URLEncoder.encode(zipFileName, "UTF-8"));
        /**
         * zip(OutputStream out, String[] paths, InputStream[] ins)：将文件流压缩到目标流中
         * @param out   目标流，压缩完成自动关闭，web 环境时，使用 response.getOutputStream() 返回 ServletOutputStream 输出即可进行网络下载.
         * @param paths 流数据在压缩文件中的路径或文件名，可以自己随意定义，会按顺序给后面的 ins 进行命名.
         * @param ins   要压缩的源，添加完成后自动关闭流
         */
        OutputStream destOutputStream = response.getOutputStream();
        ZipUtil.zip(destOutputStream, paths.toArray(new String[0]), inputStreams.toArray(new InputStream[0]));
    }

    /**
     * http://localhost:8080/fileServer/zipDownload3?fileId=1,2,3,4
     * 多个文件压缩成一个文件后下载.
     * 1、本方法使用第三方的 Apache Commons Compress 文件解压缩库
     *
     * @param fileId   ：被下载的文件ID，多个时使用逗号分割，如 1,2,3,4
     * @param response
     * @throws Exception
     */
    @GetMapping("/zipDownload3")
    public void zipDownload3(@RequestParam String fileId, HttpServletResponse response) {
        String zipFileName = "内部资料，请勿外传" + ".zip";
        Map<String, InputStream> inputStreamMap = listInputStream(fileId);
        // 通过 OutputStream 创建 zip 压缩流。如果是压缩到本地，也可以直接使用 ZipArchiveOutputStream(final File file)
        try (ServletOutputStream servletOutputStream = response.getOutputStream();
             ZipArchiveOutputStream zipArchiveOutputStream = new ZipArchiveOutputStream(servletOutputStream)) {
            // 设置文件响应头信息
            response.setContentType("content-type:octet-stream;charset=UTF-8");
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(zipFileName, "utf-8"));
            // setUseZip64(final Zip64Mode mode)：是否使用 Zip64 扩展。
            // Zip64Mode 枚举有 3 个值：
            // * Always：对所有条目使用 Zip64 扩展
            // * Never：不对任何条目使用Zip64扩展
            // * AsNeeded：对需要的所有条目使用Zip64扩展
            zipArchiveOutputStream.setUseZip64(Zip64Mode.AsNeeded);
            for (Map.Entry<String, InputStream> inputStreamEntry : inputStreamMap.entrySet()) {
                String fileName = FileUtil.getName(inputStreamEntry.getKey());
                InputStream inputStream = inputStreamEntry.getValue();
                // 使用 ByteArrayOutputStream 读取文件字节
                ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
                byte[] buffer = new byte[1024];
                int readLength;
                while ((readLength = inputStream.read(buffer)) != -1) {
                    byteArrayOutputStream.write(buffer, 0, readLength);
                }
                if (byteArrayOutputStream != null) {
                    byteArrayOutputStream.flush();
                }
                // 整个文件字节数据
                byte[] fileBytes = byteArrayOutputStream.toByteArray();
                // 用指定的名称创建一个新的 zip 条目(zip压缩实体)，然后设置到 zip 压缩输出流中进行写入.
                ArchiveEntry entry = new ZipArchiveEntry(fileName);
                zipArchiveOutputStream.putArchiveEntry(entry);
                // write(byte b[])：从指定的字节数组写入 b.length 个字节到此输出流
                zipArchiveOutputStream.write(fileBytes);
            }
            // 写入此条目的所有必要数据。如果条目未压缩或压缩后的大小超过4 GB 则抛出异常
            zipArchiveOutputStream.closeArchiveEntry();
        } catch (IOException e) {
            log.error(ExceptionUtils.getStackTrace(e));
        }
    }

    /**
     * 将文本内容写入到输出流中进行下载
     * http://localhost:8080/fileServer/generateScript
     *
     * @param response
     * @throws IOException
     */
    @RequestMapping(value = "/generateScript", method = RequestMethod.GET)
    public void generateScript(HttpServletResponse response) throws IOException {
        List<String> alterSqlList = new ArrayList<>();
        alterSqlList.add("ALTER TABLE BAS_PERSON_INFO ADD UPDATE_TIME DATE DEFAULT sysdate;");
        alterSqlList.add("ALTER TABLE BAS_PERSON_INFO_HIS ADD UPDATE_TIME DATE DEFAULT sysdate;");
        alterSqlList.add("ALTER TABLE BAS_PERSON_INFO_FORMER ADD UPDATE_TIME DATE DEFAULT sysdate;");
        OutputStream outputStream = null;
        try {
            response.setContentType("application/octet-stream");
            response.setHeader("Content-Disposition", "attachment;filename=" + System.currentTimeMillis() + ".sql");
            outputStream = new BufferedOutputStream(response.getOutputStream());
            outputStream.write("\n".getBytes());
            outputStream.write("--Add columns to the table".getBytes());
            for (String alterSql : alterSqlList) {
                outputStream.write("\n".getBytes());
                outputStream.write(alterSql.getBytes());
            }
            outputStream.write("\n".getBytes());
            outputStream.write("\n".getBytes());

            outputStream.write(("\n\n--蚩尤后裔提供技术支持 @ " + DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss")).getBytes());
        } finally {
            if (outputStream != null) {
                outputStream.flush();
                outputStream.close();
            }
        }
    }

    /**
     * 解决 HttpServletResponse OutputStream Writer 输出数据乱码
     * <p>
     * http://localhost:8080/fileServer/showInitLog1
     *
     * @param response
     * @throws IOException
     */
    @RequestMapping(value = "/showInitLog1", method = RequestMethod.GET)
    public void showInitLog1(HttpServletResponse response) throws IOException {
        // 根据初始化操作的唯一编码查询操作的完整日志
        List<String> dataList = new ArrayList<>();
        dataList.add("2023-09-20 09:08:09 检查备份表、临时表与部标表字段差异开始.");
        dataList.add("2023-09-20 09:08:10 单位扩展信息从临时表插入到正式表，影响条数0");
        dataList.add("2023-09-20 09:08:11 将需要备份的数据主键ID放入到单位基本信息临时表，影响条数=0");
        dataList.add("2023-09-20 09:08:12 同步全部区划2024本级单位信息完成.");
        OutputStream outputStream = null;
        try {
            // 指定浏览器以UTF-8解码(编码与解码必须相同，否者就会出现乱码)
            response.setHeader("content-type", "text/html;charset=utf-8");
            outputStream = new BufferedOutputStream(response.getOutputStream());
            for (String msg : dataList) {
                // 指定以UTF-8编码输出
                outputStream.write(msg.getBytes("utf-8"));
                outputStream.write("<br/>".getBytes());
            }
        } finally {
            if (outputStream != null) {
                outputStream.flush();
                outputStream.close();
            }
        }
    }

    /**
     * 解决 HttpServletResponse OutputStream Writer 输出数据乱码
     * <p>
     * http://localhost:8080/fileServer/showInitLog2
     *
     * @param response
     * @throws IOException
     */
    @RequestMapping(value = "/showInitLog2", method = RequestMethod.GET)
    public void showInitLog2(HttpServletResponse response) throws IOException {
        // 根据初始化操作的唯一编码查询操作的完整日志
        List<String> dataList = new ArrayList<>();
        dataList.add("2023-09-20 09:08:09 检查备份表、临时表与部标表字段差异开始.");
        dataList.add("2023-09-20 09:08:10 单位扩展信息从临时表插入到正式表，影响条数0");
        dataList.add("2023-09-20 09:08:11 将需要备份的数据主键ID放入到单位基本信息临时表，影响条数=0");
        dataList.add("2023-09-20 09:08:10 重置单位基本信息临时表，影响条数=241");
        dataList.add("2023-09-20 09:08:10 备份430300000 2021年以前的单位信息完成.");
        dataList.add("2023-09-20 09:08:12 同步全部区划2024本级单位信息完成.");
        OutputStream outputStream = null;
        try {
            // 指定以UTF-8编码输出
            response.setCharacterEncoding("UTF-8");
            // 指定浏览器以UTF-8解码(编码与解码必须相同，否者就会出现乱码)
            response.setContentType("text/html;charset=utf-8");
            outputStream = new BufferedOutputStream(response.getOutputStream());
            for (String msg : dataList) {
                outputStream.write(msg.getBytes());
                outputStream.write("<br/>".getBytes());
            }
        } finally {
            if (outputStream != null) {
                outputStream.flush();
                outputStream.close();
            }
        }
    }

    /**
     * 获取被下载文件
     *
     * @param fileId ：被下载文件ID
     * @return
     * @throws UnsupportedEncodingException
     */
    private File getFile(String fileId) throws UnsupportedEncodingException {
        String filePath = imageMap.get(fileId);
        if (StringUtils.isBlank(filePath)) {
            filePath = videoMap.get(fileId);
        }
        Assert.hasText(filePath, "文件ID错误，无法查询到被下载文件！");
        URL url = DownloadFileController.class.getClassLoader().getResource(filePath);
        Assert.notNull(url, fileId + "：资源文件不存在！");
        String path = url.getPath();
        // 对地址进行解码，以得到原始路径，防止中文乱码.
        path = URLDecoder.decode(path, "utf-8");

        return new File(path);
    }

    /**
     * 获取被下载文件路径
     *
     * @param fileIds ：被下载文件ID，多个时使用逗号分割，如 1,2,3,4
     * @return
     * @throws UnsupportedEncodingException
     */
    private List<Path> listPath(String fileIds) throws UnsupportedEncodingException {
        List<Path> filePaths = new ArrayList<>();
        String[] split = fileIds.split(",");
        for (String fileId : split) {
            File file = this.getFile(fileId);
            filePaths.add(file.toPath());
        }
        return filePaths;
    }

    private Map<String, InputStream> listInputStream(String fileIds) {
        Map<String, InputStream> inputStreamMap = new HashMap<>();
        String[] split = fileIds.split(",");
        for (String fileId : split) {
            String filePath = imageMap.get(fileId);
            if (StringUtils.isBlank(filePath)) {
                filePath = videoMap.get(fileId);
            }
            Assert.hasText(filePath, "文件ID错误，无法查询到被下载文件！");
            InputStream inputStream = DownloadFileController.class.getClassLoader().getResourceAsStream(filePath);
            Assert.notNull(inputStream, fileId + "：资源文件不存在！");
            inputStreamMap.put(filePath, inputStream);
        }
        return inputStreamMap;
    }

}
